package com.swan.security.auto;

import com.swan.security.interceptor.AuthLoginInterceptor;
import com.swan.security.interceptor.AuthPermissionInterceptor;
import com.swan.security.interceptor.SecurityLogIdInjector;
import com.swan.security.auto.config.SecurityConfig;
import com.swan.security.security.*;
import com.swan.security.exception.BadCredentialsExceptionHandler;
import com.swan.security.token.JwtTokenManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;


/***
 *  分布式系统中: 用户认证和接口鉴权应该封装在 Gateway 层，解析出用户信息后，传递给下游服务(比如添加到header)中，下游从header中获取用户信息
 *  本项目参考分布式设计，将用户认证和鉴权封装在 web 模块儿，解析出的用户信息，存储在 AuthenticationUtil(ThreadLocal实现) 中，供下游模块获取。
 *      如果将此项目改造为分布式系统，则将 AuthenticationUtil 内部实现由 ThreadLocal 替换为从 HttpRequest 的header中获取即可
 */
@Configuration
@EnableWebSecurity
public class SecurityAutoConfig {

    @Bean
    public SecurityConfig securityConfig(){
        return new SecurityConfig();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable)                  // 前后端分离，通常不需要开启 CSRF 防护
            .formLogin(form -> form.disable())                                      // 表单登录: security 默认使用表单认证，对于前后端分离项目，可以禁用
            .addFilterBefore(authLoginInterceptor(), UsernamePasswordAuthenticationFilter.class)
            .addFilter(securityLogIdInjector())
            .authorizeHttpRequests(auth -> {
                    for (String publicPath : securityConfig().getPublicPath()) {
                        auth.requestMatchers(publicPath).permitAll();
                    }
                    auth.anyRequest().access(authPermissionInterceptor());       // 其他请求走自定义认证
                }
            )
            .logout(logout -> logout
                    .logoutUrl(securityConfig().getLogoutUrl())      // 自定义 logout 逻辑
                    .logoutSuccessHandler(securityLogoutHandler())
                    .permitAll()                                                    // 登出: 所有用户都可以
            );

        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(authenticationProvider);
    }

    @Bean
    public BadCredentialsExceptionHandler badCredentialsExceptionHandler(){
        return new BadCredentialsExceptionHandler();
    }

    @Bean
    public SecurityUserDetailSupplier securityUserDetailSupplier(){
        return new SecurityUserDetailSupplier();
    }

    @Bean
    public SecurityLogIdInjector securityLogIdInjector(){
        return new SecurityLogIdInjector();
    }

    @Bean
    public SecurityLogoutHandler securityLogoutHandler(){
        return new SecurityLogoutHandler();
    }

    @Bean
    public AuthLoginInterceptor authLoginInterceptor(){
        return new AuthLoginInterceptor();
    }

    @Bean
    public AuthPermissionInterceptor authPermissionInterceptor(){
        return new AuthPermissionInterceptor();
    }

    @Bean
    public JwtTokenManager jwtTokenManager(){
        return new JwtTokenManager();
    }

}
